package com.sp.fresh_produce.web;

import com.sp.fresh_produce.model.dao.UserMapper;
import com.sp.fresh_produce.model.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * 记住我服务：通过签名 Cookie 实现 5 天内免登录。
 * Cookie 格式：base64(userId:expires:signature)
 * signature = HMAC-SHA256(secret, userId + ":" + expires)
 */
@Component
public class RememberMeService {

    public static final String COOKIE_NAME = "FP_REMEMBER_ME";
    private static final String HMAC_ALGO = "HmacSHA256";

    private final UserMapper userMapper;
    private final String secret;
    private final int maxAgeSeconds;
    private final String cookieDomain;
    private final String cookiePath;
    private final boolean cookieSecure;
    private final boolean cookieHttpOnly;

    public RememberMeService(UserMapper userMapper,
                             @Value("${security.rememberme.secret:change_me}") String secret,
                             @Value("${security.rememberme.max-age-days:5}") int maxAgeDays,
                             @Value("${security.rememberme.cookie-domain:}") String cookieDomain,
                             @Value("${security.rememberme.cookie-path:/}") String cookiePath,
                             @Value("${security.rememberme.cookie-secure:false}") boolean cookieSecure,
                             @Value("${security.rememberme.cookie-httponly:true}") boolean cookieHttpOnly) {
        this.userMapper = userMapper;
        this.secret = secret;
        this.maxAgeSeconds = Math.max(1, maxAgeDays) * 24 * 60 * 60;
        this.cookieDomain = cookieDomain == null ? "" : cookieDomain.trim();
        this.cookiePath = cookiePath == null || cookiePath.isBlank() ? "/" : cookiePath;
        this.cookieSecure = cookieSecure;
        this.cookieHttpOnly = cookieHttpOnly;
    }

    public void onLoginSuccess(HttpServletResponse response, Integer userId) {
        if (userId == null) return;
        long expires = Instant.now().getEpochSecond() + maxAgeSeconds;
        String data = userId + ":" + expires;
        String sig = hmac(data);
        String token = Base64.getUrlEncoder().withoutPadding()
                .encodeToString((data + ":" + sig).getBytes(StandardCharsets.UTF_8));
        Cookie cookie = new Cookie(COOKIE_NAME, token);
        cookie.setMaxAge(maxAgeSeconds);
        cookie.setPath(cookiePath);
        if (!cookieDomain.isEmpty()) cookie.setDomain(cookieDomain);
        cookie.setSecure(cookieSecure);
        cookie.setHttpOnly(cookieHttpOnly);
        response.addCookie(cookie);
    }

    public void clearRememberMe(HttpServletResponse response) {
        Cookie cookie = new Cookie(COOKIE_NAME, "");
        cookie.setMaxAge(0);
        cookie.setPath(cookiePath);
        if (!cookieDomain.isEmpty()) cookie.setDomain(cookieDomain);
        cookie.setSecure(cookieSecure);
        cookie.setHttpOnly(cookieHttpOnly);
        response.addCookie(cookie);
    }

    /**
     * 如果会话无用户，尝试从 RememberMe Cookie 解析并加载用户。
     * 成功时返回用户对象；失败返回 null。
     */
    public User autoLogin(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return null;
        for (Cookie c : cookies) {
            if (!COOKIE_NAME.equals(c.getName())) continue;
            String value = c.getValue();
            if (value == null || value.isBlank()) return null;
            try {
                String decoded = new String(Base64.getUrlDecoder().decode(value), StandardCharsets.UTF_8);
                String[] parts = decoded.split(":");
                if (parts.length != 3) return null;
                Integer userId = Integer.valueOf(parts[0]);
                long expires = Long.parseLong(parts[1]);
                String sig = parts[2];
                if (Instant.now().getEpochSecond() > expires) return null;
                String expected = hmac(parts[0] + ":" + parts[1]);
                if (!constantTimeEquals(sig, expected)) return null;
                User user = userMapper.selectByPrimaryKey(userId);
                if (user == null) return null;
                user.setPassword(null);
                return user;
            } catch (Exception ignore) {
                return null;
            }
        }
        return null;
    }

    private String hmac(String data) {
        try {
            Mac mac = Mac.getInstance(HMAC_ALGO);
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGO));
            byte[] out = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getUrlEncoder().withoutPadding().encodeToString(out);
        } catch (Exception e) {
            throw new IllegalStateException("HMAC failure", e);
        }
    }

    private boolean constantTimeEquals(String a, String b) {
        if (a == null || b == null) return false;
        if (a.length() != b.length()) return false;
        int r = 0;
        for (int i = 0; i < a.length(); i++) {
            r |= a.charAt(i) ^ b.charAt(i);
        }
        return r == 0;
    }
}



